#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<cstdio>
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <time.h>
using namespace std;
#include <winsock2.h>
#include <regex>
#pragma comment(lib,"ws2_32.lib")

class Exception {
    string msg;
public:
    Exception(string s) {
        msg = s;
    }
    void Print() {
        cout << "Exception: " << msg << endl;
    }
};
class TCPConnector {
    SOCKET sock;
public:
    TCPConnector(string destIP, int port) {
        try {
            sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if (sock == INVALID_SOCKET) throw 2;
            sockaddr_in sockad;
            sockad.sin_family = AF_INET;
            sockad.sin_addr.S_un.S_addr = inet_addr(destIP.c_str());
            sockad.sin_port = htons(port);
            if (connect(sock, (sockaddr*)(&sockad), sizeof(sockad)) == SOCKET_ERROR)
                throw 3;
        }
        catch (int e) {
            if (e == 2)
                if (e == 3) {
                    int iResult = closesocket(sock);
                    if (iResult == SOCKET_ERROR)
                        wprintf(L"closesocket function failed with error: %d\n", WSAGetLastError());
                }
            printf("Error Code:%d\n", WSAGetLastError());
            throw Exception("create TCP connection failed");
        }
    }
    bool Send(const string & s) {
        const char* buf = s.c_str();
        int cnt = s.length() / 7000;
        int i = 0;
        int pd = cnt / 50 + 1;
        for (; i < cnt; i++) {
            if (send(sock, buf + i * 7000, 7000, 0) == SOCKET_ERROR) { return false; }
            if (i % pd == 0) printf("#");

        }
        if (s.length() % 7000 != 0) {
            if (send(sock, buf + i * 7000, s.length() % 7000, 0) == SOCKET_ERROR) {
                return false;
            }
        }
        return true;
    }
    string Receive(int charLength = -1, string delimit = "\r\n.\r\n") { // charLength >= len
        if (charLength < 0) {
            char* buf = new char[7005];
            string s = "";
            int len;
            int i = 0;
            while ((len = recv(sock, buf, 7000, 0)) != SOCKET_ERROR) {
                buf[len] = '\0';
                i++;
                if (i == 50) {
                    printf("#");
                    i = 0;
                }
                string t = buf;
                s += t;
                if (s.substr(s.length() - 5) == delimit) break;
            }
            delete [] buf;
            return s;
        }
        else {
            char* buf = new char[charLength + 5];
            string s;
            int len;
            if ((len = recv(sock, buf, charLength, 0)) != SOCKET_ERROR) {
                buf[len] = '\0';
                s = buf;
                delete [] buf;
                return s;
            }
        }
    }
    ~TCPConnector() {
        int iResult = closesocket(sock);
        if (iResult == SOCKET_ERROR)
            wprintf(L"closesocket function failed with error: %d\n", WSAGetLastError());
    }
};
string GetEmailServerIP(string protoName, string emailAddress) {
    string serverName = protoName + "." + emailAddress.substr(emailAddress.find("@") + 1);
    HOSTENT* hostent = gethostbyname(serverName.c_str());
    if (hostent == NULL) throw Exception("gethostbyname failed with " + serverName);
    if (hostent->h_addrtype != AF_INET) throw Exception("hostent addrtype is not AF_INET");
    in_addr addr;
    if (hostent->h_addr_list[0] == 0) throw Exception("hostent addr_list[0] equals zero");
    addr.S_un.S_addr = *(u_long*)hostent->h_addr_list[0];
    return inet_ntoa(addr);
}
string GetUserFromEmailAddress(string emailAddress) {
    return emailAddress.substr(0, emailAddress.find("@"));
}
//+OK 20 72782
void SplitString(vector<string>& res, string s, char c) {
    if (s.length() > 0) {
        int start = 0;
        if (s[0] == c) start = 1;
        for (int i = start + 1; i < s.length(); i++) {
            if (s[i] == c) {
                res.push_back(s.substr(start, i - start));
                start = i + 1;
            }
        }
        if (start != s.length()) res.push_back(s.substr(start));
    }
}
//输出的字符如果超过76个字符就会自动换行
bool EnCode(string& res, string filename, string rule) {

    FILE* fp = fopen(filename.c_str(), "rb");
    if (fp == NULL) return false;
    fseek(fp, 0, SEEK_END);
    int len = ftell(fp);
    char* out = new char[(int)(1.5 * len)];
    memset(out, 0, len);
    fseek(fp, 0, SEEK_SET);
    unsigned char in[3];
    memset(in, 0, 3);
    int i = 0;
    string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int j = 0;
    int c = 0;
    while ((j = fread(in, 1, 3, fp)) == 3) {
        out[i] = a[(char)(in[0] >> 2)];
        out[i + 1] = a[(char)((in[0] & 0x03) << 4 | in[1] >> 4)];
        out[i + 2] = a[(char)((in[1] & 0x0f) << 2 | in[2] >> 6)];
        out[i + 3] = a[(char)(in[2] & 0x3f)];
        i += 4;
        memset(in, 0, 3);

        c += 4;
        if (c == 76) {
            out[i] = '\r';
            out[i + 1] = '\n';
            i += 2;
            c = 0;
        }

    }
    if (j != 0) {
        out[i] = a[(char)(in[0] >> 2)];
        out[i + 1] = a[(char)((in[0] & 0x03) << 4 | in[1] >> 4)];
        out[i + 2] = a[(char)((in[1] & 0x0f) << 2 | in[2] >> 6)];
        out[i + 3] = a[(char)(in[2] & 0x3f)];
        if (j == 2) out[i + 3] = '=';
        if (j == 1) {
            out[i + 3] = '=';
            out[i + 2] = '=';
        }
        out[i + 4] = '\0';
    }
    else out[i] = '\0';
    res = out;
    fclose(fp);
    return true;
}
bool DeCode(string filename, const string& src, string rule) {
    char buf1[100];
    GetModuleFileNameA(NULL, buf1, 100);
    string t = string(buf1);
    string PATH = t.substr(0, t.rfind('\\'))+"\\"+filename;
    remove(&PATH[0]);

    FILE* fp = fopen(PATH.c_str(), "wb");
    if (fp == NULL) {
        printf("can't open %s\n", filename.c_str());
        return false;
    }
    char a[127];
    for (int i = 'a', j = 'A', k = 0; i <= 'z'; i++, j++, k++) {
        a[i] = k + 26;
        a[j] = k;
    }
    for (int i = '0', j = 52; i <= '9'; i++, j++) a[i] = j;
    a['+'] = 62;
    a['/'] = 63;
    a['='] = 0;
    int len = src.length();
    int i = 3;
    unsigned char b[4];
    char buf[3];
    for (; i < len; i += 4) {
        b[0] = a[src[i - 3]];
        b[1] = a[src[i - 2]];
        b[2] = a[src[i - 1]];
        b[3] = a[src[i]];

        buf[0] = (b[0] << 2) | (b[1] >> 4);
        buf[1] = (b[1] << 4) | (b[2] >> 2);
        buf[2] = (b[2] << 6) | b[3];
        if (src[i] != '=') fwrite(buf, 1, 3, fp);
        else {
            if (src[i] == '=' && src[i - 1] == '=') fwrite(buf, 1, 1, fp);
            else if (src[i] == '=') fwrite(buf, 1, 2, fp);
        }
    }
    fclose(fp);
    return true;
}
void MyBoundaryFind(string src, string boundary, vector<int>& index) {
    int len = boundary.length();
    int pre = src.find(boundary);
    src = src.substr(pre + len);
    index.push_back(pre);
    int i;
    while ((i = src.find(boundary)) != string::npos) {
        pre = pre + len + i;
        index.push_back(pre);
        src = src.substr(i + len);
    }
}
string GetEnCodeContent(const string& s, string encoding = "base64") {
    smatch sm;
    regex_search(s,sm,regex("(\\r?\\n)\\1"));
    int i = sm.position();
    if (s[i] == '\r') i = i + 4;
    else i = i + 3;
    if (encoding != "base64") return s.substr(i);
    int slen = s.length();

    int k = i;
    for (; s[k] != '\r'; k++);
    int linelen = k - i;
    string ret = "";
    for (; i < slen; i += linelen + 2) {
        if (i + linelen > slen) {
            int j = i;
            for (; s[j] != '\r' && s[j] != '\n'; j++);
            ret += s.substr(i, j - i);
        }
        else ret += s.substr(i, linelen);
    }
    return ret;
}
bool DeQuoPri(const string& _src, string filename) {
    char buf1[100];
    GetModuleFileNameA(NULL, buf1, 100);
    string t = string(buf1);
    string PATH = t.substr(0, t.rfind('\\'))+"\\"+filename;
    remove(&PATH[0]);

    FILE* fp = fopen(PATH.c_str(), "wb");
    if (fp) {
        int len = _src.length();
        unsigned char* buffer = new unsigned char[len];
        char* src = new char[len];
        memcpy(src, _src.c_str(), len);
        int b = 0;
        int pt = 0;
        auto f = [](char c) ->int { return c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - '0'; };
        auto isUpperAlnum = [](char a) {return (a >= 'A' && a <= 'F') || (a >= '0' && a <= '9') ? true : false; };
        for (int i = 0; i < len;) {
            if (src[i] == '=' && src[i + 1] == '\n') {
                memcpy(buffer + pt, src + b, i - b);
                pt = pt + i - b;
                b = i = i + 2;
                continue;
            }
            if (src[i] == '=' && src[i + 1] == '\r' && src[i + 2] == '\n') {
                memcpy(buffer + pt, src + b, i - b);
                pt = pt + i - b;
                b = i = i + 3;
                continue;
            }
            if (src[i] == '=' && isUpperAlnum(src[i + 1]) && isUpperAlnum(src[i + 2])) {
                memcpy(buffer + pt, src + b, i - b);
                pt = pt + i - b;
                unsigned char n = 0;
                n = f(src[i + 1]) * 16 + f(src[i + 2]);
                buffer[pt] = n;
                pt = pt + 1;
                b = i = i + 3;
                continue;
            }
            i++;
        }
        if (b < len) {
            memcpy(buffer + pt, src + b, len - b);
            pt = pt + len - b;
        }
        fwrite(buffer, 1, pt, fp);
        delete[] buffer;
        delete[] src;
        fclose(fp);
        return true;
    }
    else return false;
}
string GetStringFromFile(string charset) {
    string ret = "";
    if ( charset=="utf-8") {
        //temp code by utf-8
        FILE* fp = _wfopen(L"temp", L"r,ccs=utf-8");
        wchar_t buf[150];
        if (fp != NULL) fwscanf(fp, L"%s", buf);
        fclose(fp);
        char mbBuf[300];
        WideCharToMultiByte(CP_ACP, 0, buf, -1, mbBuf, 300, "#", 0);
        ret = string(mbBuf);
    }
    else {
        FILE* fp = fopen("temp", "rb");
        char buf[300];
        if (fp) {
            fseek(fp, 0, SEEK_END);
            int size = ftell(fp);
            fseek(fp, 0, SEEK_SET);
            fread(buf, 1, size, fp);
            buf[size] = '\0';
        }
        fclose(fp);
        ret = string(buf);
    }
    return ret;
}
//Subject: =?UTF-8?B?572R5piT6YKu566x77yM6YKj5Lqb5L2g?=
// =?UTF-8?B?55+l6YGT5ZKM5LiN55+l6YGT55qE5LqL?=

//"=?UTF-8?B?5LqU6YKR5aSn5a2m572R57uc5a6J5YWo5pyf?=
// =?UTF-8?B?5pyr5aSN5Lmg6aKYKDEpLmRvY3g=?="

//filename*=utf-8''%E6%9D%A8zi.jpg
//=?utf-8?B?5oeC5rOV5a6I5rOV55yL5pe26Ze0?=
//=?gb18030?B?zrTD/MP7My5jcHA=?=
//20200610145415zecyab.jpg
//From: "=?gb18030?B?sfnQxMrExsazpNK5zOw=?=" <xjqxz4yth@qq.com>
//To: "=?gb18030?B?MTU4MTk4MjcyMTI=?=" <15819827212@189.cn>
string ParseString(string s) {
    string ret = "";
    if (s.length() > 0) {
        string out = regex_replace(s, regex("[\"\\r\\n\\t]"), " ");
        out = regex_replace(out, regex("(\\s)\\1+"), " ");
        vector<string> res;
        SplitString(res, out, ' ');
        for (int i = 0; i < res.size(); i++) {
            if (res[i].size() > 0 && res[i][0] == '=') {
                vector<string> v;
                SplitString(v, res[i], '?');
                if (v.size() > 1) {
                    if (v[2][0] == 'B' || v[2][0] == 'b') {
                        if (v[1][0] == 'U' || v[1][0] == 'u') {
                            DeCode("temp", v[3], "base64");
                            ret += GetStringFromFile("utf-8");
                        }
                        else {
                            DeCode("temp", v[3], "base64");
                            ret += GetStringFromFile("ansi");
                        }
                    }
                    else {
                        if (v[1][0] == 'U' || v[1][0] == 'u') {
                            DeQuoPri(v[3],"temp");
                            ret += GetStringFromFile("utf-8");
                        }
                        else {
                            DeQuoPri(v[3], "temp");
                            ret += GetStringFromFile("ansi");
                        }
                    }
                }
                else ret += res[i];
            }
            else ret += res[i];
        }
    }
    return ret;
}
string GetValueByKey(string s, string key) {
    int k = s.find(key);
    if (k == string::npos) return "";
    for (; s[k] != ':'; k++);
    k = k + 1;
    smatch sm;
    regex_search(s.cbegin() + k, s.cend(), sm, regex("\\r?\\n[\\w\\r\\n]"));
    return s.substr(k, sm.position());
}
string GetFieldInValue(string value, string f) {
    string ret = "";
    if (value.find(f) != string::npos) {
        int s = value.find(f) + f.length() + 1; // s 指向'=' '/' '*' 后的第一个字符
        int e = s;
        for (; e < value.length() && value[e] != ';'; e++);
        ret = value.substr(s, e - s);
    }
    return ret;
}
class MIMEContent {
    vector<MIMEContent*> items;
    bool isMultipart;
    string boundary;  //not NULL represents multipart
    string contentType;
    string contentTransferEnCodeing;
    string contentDisposition;
    string contentString;
private:

public:
    MIMEContent(string contentType, string contentTransferEnCodeing,
        string contentFileName = "", string contentDisposition = "") {
        //Content-Type: text/plain;
        //charset="gb18030"
        //Content-Transfer-Encoding: base64

        //Content-Type: image/jpeg;
        //  name="mmexport1590651798523.jpg"
        //Content-Transfer-Encoding: base64
        //Content-Disposition: attachment;
        //  filename="mmexport1590651798523.jpg";
        this->contentType = contentType;
        this->contentTransferEnCodeing = contentTransferEnCodeing;
        this->contentDisposition = contentDisposition;
        this->contentString = "";

        if (contentFileName == "") {
            this->isMultipart = true;
            char buf[50];
            sprintf(buf, "----=_MYEMAIL_Part_%d\0", time(NULL));
            this->contentType = this->contentType + "\r\n\tboundary=\"" + string(buf) + "\"";
            this->boundary = ("--") + string(buf);
        }
        else if (contentDisposition == "") {
            //content
            this->isMultipart = false;
            EnCode(this->contentString, contentFileName, this->contentTransferEnCodeing);
        }
        else {
            //attachment
            this->isMultipart = false;
            //--------
            FILE* fp = fopen("temp", "w");
            fprintf(fp, "%s", contentFileName.c_str());
            fclose(fp);
            string res;
            EnCode(res, "temp", "base64");
            string t = "=?gb18030?B?" + res + "?=";
            //--------
            this->contentDisposition += ("filename=\"" + t + "\";");
            EnCode(this->contentString, contentFileName, this->contentTransferEnCodeing);
        }

    }
    MIMEContent(const string & MIMEString) {
        this->contentType = GetValueByKey(MIMEString, "Content-Type");
        if (this->contentType.find("multipart") == string::npos &&
            this->contentType.find("Multipart") == string::npos) {
            this->contentTransferEnCodeing = GetValueByKey(MIMEString, "Content-Transfer-Encoding");
            this->contentDisposition = GetValueByKey(MIMEString, "Content-Disposition");
            if (contentTransferEnCodeing.find("quoted-printable") != string::npos )
                this->contentString = GetEnCodeContent(MIMEString, "others"); // \r\n\r\n
            else this->contentString = GetEnCodeContent(MIMEString);
            this->isMultipart = false;
        }
        else {
            this->isMultipart = true;
            this->boundary = regex_replace(GetFieldInValue(contentType, "boundary"), regex("\""), "");
            this->contentTransferEnCodeing = GetValueByKey(MIMEString, "Content-Transfer-Encoding");

            vector<int> index;
            MyBoundaryFind(MIMEString, "--" + boundary, index);
            int count = index.size() - 1;
            for (int i = 0; i < count; i++) {
                items.push_back(new MIMEContent(MIMEString.substr(index[i], index[i + 1] - index[i])));
            }
        }
    }
    void SaveToFile(string subject) {
        if (!isMultipart) {
            string filename = GetFieldInValue(contentType, "name");
            filename = ParseString(filename);
            if (filename == "") filename = ParseString(GetFieldInValue(contentDisposition, "filename"));
            //如果在 Content-Type找到name字段 或者 Content-Disposition中找到filename字段，
            //则按照给出的名字命名文件
            if (filename == "") {// 是一个邮件内容
                filename = "mail";
                string t = GetFieldInValue(contentType, "text");
                if (t == "") t = GetFieldInValue(contentType, "Text");
                if (t == "") t = "无法识别类型";
                filename += ("." + t);
            }
            if (contentTransferEnCodeing.find("base64") != string::npos
                || contentTransferEnCodeing.find("Base64") != string::npos) {
                DeCode(filename, contentString, "base64");
                printf("had saved %s\n", filename.c_str());
            }
            else {//不是base64，都认为是QuoPri
                DeQuoPri(contentString, filename);
                printf("had saved %s\n",filename.c_str());
            }
        }
        else {
            for (unsigned int i = 0; i < items.size(); i++) {
                items[i]->SaveToFile(subject);
            }
        }
    }
    void AddMIMEContent(MIMEContent* c) {
        items.push_back(c);
    }
    string ToString() {
        string s;
        if (!isMultipart) {
            string add = "";
            if (contentDisposition != "") add = "\r\nContent-Disposition: " + contentDisposition;
            s = "\r\nContent-Type: " + contentType + "\r\n" + "Content-Transfer-Encoding: " +
                contentTransferEnCodeing + add + "\r\n\r\n"
                + contentString + "\r\n";

        }
        else {
            s = "Content-Type: " + contentType + "\r\n" + "Content-Transfer-Encoding: "
                + contentTransferEnCodeing + "\r\n\r\n";
            for (int i = 0; i < items.size(); i++) {
                s += boundary;
                s += items[i]->ToString();
            }
            s += boundary + "--\r\n";
        }
        return s;
    }
};
string GetLocalCST() {
    time_t rawtime;
    time(&rawtime);
    struct tm* info = localtime(&rawtime);
    //Wed, 10 Jun 2020 14:55:15 +0800 (CST)
    string week[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
    string month[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec" };
    char buf[50];
    sprintf(buf, "%s, %d %s %d %02d:%02d:%02d\0", week[info->tm_wday].c_str(),
        info->tm_mday, month[info->tm_mon].c_str(), info->tm_year + 1900,
        info->tm_hour, info->tm_min, info->tm_sec);
    return string(buf) + " +0800 (CST)";

}
class Email {
    string topStr;
    string allStr;
    string from;
    string to;
    string date;
    string subject;
    MIMEContent* content;

public:
    Email() {
        content = NULL;
    }
    Email& FillTopInfo(const string & s) {
        char buf[100];
        GetModuleFileNameA(NULL, buf, 100);
        string t1 = string(buf);
        string PATH = t1.substr(0, t1.rfind('\\'))+"\\list.txt";


        this->topStr = s;
        this->from = ParseString(GetValueByKey(s, "\nFrom"));
        this->to = ParseString(GetValueByKey(s, "\nTo"));
        this->date = GetValueByKey(s, "\nDate");
        this->subject = ParseString(GetValueByKey(s, "\nSubject"));
        string t = "\\/*:?\"<>|";
        for (int i = 0; i < subject.length(); i++) {
            if (t.find(subject[i]) != string::npos) subject[i] = '_';
        }
        ofstream outfile(PATH,ios::app);

        //
        outfile<<from.c_str()<<'\t'<<to.c_str()<<'\t'<<date.c_str()<<'\t'<<subject.c_str()<<'\n';
        //
        return *this;
    }
    Email& PrintTopInfo() {
        //printf("ID\tFrom\tTo\tDate\tSubject\tCount=%d\n",emailCount);
        printf("%s\t%s\t%s\t%s\n", from.c_str(), to.c_str(), date.c_str(), subject.c_str());
        return *this;
    }
    Email& FillAll(const string & s) {
        this->allStr = s;
        FillTopInfo(s);
        return *this;
    }
    Email& Save() {
        content = new MIMEContent(allStr.substr(allStr.find("\nContent-Type:")));
        content->SaveToFile(this->subject);
        return *this;
    }
    Email& SetFrom(string srcEmailAddress) {
        this->from = srcEmailAddress;
        return *this;

    }
    Email& SetTo(string destEmailAddress) {
        this->to = destEmailAddress;
        return *this;
    }
    Email& SetSubject(string s) {
        //=?gb18030?B?zrTD/MP7My5jcHA=?=
        FILE* fp = fopen("temp", "w");
        fprintf(fp, "%s", s.c_str());
        fclose(fp);
        string res;
        EnCode(res, "temp", "base64");
        this->subject = "=?gb18030?B?" + res + "?=";
        return *this;
    }
    Email& SetMIMEContent(MIMEContent* cont) {
        this->content = cont;
        return *this;
    }
    string BuidEmailString() {
        string s = "";
        s = "MIME-Version: 1.0\r\nFrom: " + from + "\r\nTo: " + to + "\r\nSubject: " + subject + "\r\nDate: " + GetLocalCST() + "\r\n"
            + content->ToString() + "\r\n.\r\n";
        return s;
    }

};
class POP3Connector : public TCPConnector {
    string emailAddress;
    string password;
    static const int port = 110;

public:
    POP3Connector(string emailAddress, string password) :TCPConnector(GetEmailServerIP("pop", emailAddress), port) {
        this->emailAddress = emailAddress;
        this->password = password;
        string s = "user " + GetUserFromEmailAddress(emailAddress) + "\r\n";
        Receive(2000);   //
        if (Send(s)) {
            string ret = Receive(2000);
            if (ret.length() > 0 && ret[0] == '+') {
                s = "pass " + password + "\r\n";
                if (Send(s)) {
                    ret = Receive(2000);
                    if (ret.length() <= 0 || ret[0] != '+') throw Exception(ret);
                }
                else throw Exception("Send failed with CML " + s);
            }
            else throw Exception(ret);
        }
        else throw Exception("Send failed with CML " + s);
    }
    void List(int count = -1) {
        char buf[100];
        GetModuleFileNameA(NULL, buf, 100);
        string t = string(buf);
        string PATH = t.substr(0, t.rfind('\\'))+"\\list.txt";



        ofstream outfile(PATH, ios::trunc);
        int emailCount = 0;
        if (Send("stat\r\n")) {
            string ret;
            ret = Receive(100);
            if (ret.length() > 0 && ret[0] == '+') {
                vector<string> res;
                SplitString(res, ret, ' ');
                sscanf(res[1].c_str(), "%d\n", &emailCount);

                //
                outfile<< "ID\tFrom\tTo\tDate\tSubject\tCount=" << "0"+emailCount<<'\n';
                //
                outfile.close();

                printf("ID\tFrom\tTo\tDate\tSubject\tCount=%d\n", emailCount);
                if (count == -1) count = emailCount;
                if (count > emailCount) count = emailCount;
                for (int i = emailCount; i > emailCount - count; i--) {
                    char buf[20];
                    sprintf(buf, "%d", i);
                    printf("%d\t", i);

                    //
                    ofstream outfile(PATH, ios::app);
                    outfile<<i<<'\t';
                    outfile.close();
                    //
                    if (Send("top " + string(buf) + " 1\r\n")) {
                        string ret = Receive(-1);
                        if (ret.length() > 0 && ret[0] == '+') {
                            Email().FillTopInfo(ret).PrintTopInfo();
                        }
                        else throw Exception(ret);
                    }
                    else throw Exception("send top failed");

                }
            }
            else throw Exception(ret);
        }
        else throw Exception("send list failed");
    }
    void Retr(int id) {
        char buf[20];
        sprintf(buf, "%d", id);
        if (Send("retr " + string(buf) + "\r\n")) {
            printf("receiving...\n");
            string ret = Receive(-1);
            if (ret.length() > 0 && ret[0] == '+') {
                printf("receive success!\n");
                Email().FillAll(ret).PrintTopInfo().Save();
            }
            else throw Exception(ret);
        }
        else throw Exception("send retr failed");
    }
    ~POP3Connector() {

    }
};

class SMTPConnector : public TCPConnector {
    string emailAddress;
    string password;
    static const int port = 25;
    string SMTPClientDomain;
public:
    SMTPConnector(string emailAddress, string password) :TCPConnector(GetEmailServerIP("smtp", emailAddress), port) {
        this->SMTPClientDomain = "myemail-client";
        this->emailAddress = emailAddress;
        this->password = password;
        Receive(100); //
        if (Send("ehlo " + SMTPClientDomain + "\r\n")) {
            string ret = Receive(200);
            if (ret.length() > 0 && ret.substr(0, 3) == "250") {

                AuthLogin();

            }
            else throw Exception(ret);
        }
        else throw Exception("send ehlo failed");
    }
    bool SendEmail(string destEmailAddress, const string & content) {

        if (Send("mail from: <" + emailAddress + ">\r\n")) {

            string ret = Receive(50);
            if (ret.length() > 0 && ret.substr(0, 3) == "250") {
                if (Send("rcpt to: <" + destEmailAddress + ">\r\n")) {

                    ret = Receive(50);
                    if (ret.length() > 0 && ret.substr(0, 3) == "250") {

                        if (Send("data\r\n")) {
                            ret = Receive(50);
                            if (ret.length() > 0 && ret.substr(0, 3) == "354") {
                                printf("sending...\n");
                                if (Send(content)) {
                                    ret = Receive(200);
                                    if (ret.length() > 0 && ret.substr(0, 3) == "250") { return true; }
                                    else throw Exception(ret);
                                }
                                else throw Exception("send content failed");
                            }
                            else throw Exception(ret);
                        }
                        else throw Exception("send data failed");
                    }
                    else throw Exception(ret);
                }
                else throw Exception("rcpt to failed");
            }
            else throw Exception(ret);
        }
        else throw Exception("mail from failed");
        return false;
    }
private:
    void AuthLogin() {

        if (Send("auth login\r\n")) {

            string ret = Receive(50);
            if (ret.length() > 0 && ret.substr(0, 3) == "334") {
                string encodeStr;
                FILE* temp = fopen("temp", "w");
                string user = GetUserFromEmailAddress(emailAddress);
                fwrite(user.c_str(), 1, user.length(), temp);
                fclose(temp);
                EnCode(encodeStr, "temp", "Base64");
                if (Send(encodeStr + "\r\n")) {
                    ret = Receive(50);
                    if (ret.length() > 0 && ret.substr(0, 3) == "334") {
                        temp = fopen("temp", "w");
                        fwrite(password.c_str(), 1, password.length(), temp);
                        fclose(temp);
                        EnCode(encodeStr, "temp", "Base64");
                        if (Send(encodeStr + "\r\n")) {
                            printf("auth login success!\n");
                            ret = Receive(50);
                            if (!(ret.length() > 0 && ret.substr(0, 3) == "235"))
                                throw Exception(ret);
                        }
                        else throw Exception("send password failed in auth login");
                    }
                    else throw Exception("a2" + ret);
                }
                else throw Exception("send user failed in auth login");
            }
            else throw Exception("a3" + ret);
        }
        else throw Exception("send auth login failed");
    }
};
/*
    myemail bind username@xxx.xx password

    myemail bind status

    myemail list [count]

    myemail retr id

    myemail send username@xxxx.xx contentfilename [utf-8] [attachmentfilename]
*/
string PATH;
void SaveInfoToFile(string emailAddress, string password) {
    FILE* fp = fopen((PATH + "\\myemail.conf").c_str(), "w");
    if (fp != NULL)
        fprintf(fp, "%s\n%s", emailAddress.c_str(), password.c_str());
    else throw Exception("can't create myemail.conf in " + PATH + "; maybe you don't have permission");
    fclose(fp);
}
bool GetInfoFromFile(string& emailAddress, string& password) {
    char addr[100];
    char pass[100];
    FILE* fp = fopen((PATH + "\\myemail.conf").c_str(), "r");
    if (fp != NULL) {
        fscanf(fp, "%s\n%s", addr, pass);
        fclose(fp);
        emailAddress = addr;
        password = pass;
        return true;
    }
    else return false;
}
//222114
bool isNumber(string s) {
    if (s.length() == 0) return false;
    for (int i = 0; i < s.length(); i++) {
        if (s[i] < '0' || s[i]>'9') return false;
    }
    return true;
}
//int main(int argc, char* argv[]) {
int fx(int argc, char* argv[]){
    char buf[100];
    GetModuleFileNameA(NULL, buf, 100);
    string t = string(buf);
    PATH = t.substr(0, t.rfind('\\'));
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed: %d\n", iResult);
        return 1;
    }
    if (argc == 4 && !strcmp(argv[1], "bind")) {
        try {
            POP3Connector pop3(argv[2], argv[3]);
            SaveInfoToFile(argv[2], argv[3]);
            printf("binding success!\n");
        }
        catch (Exception e) {
            e.Print();
            return 0;
        }
    }
    else if (argc == 3 && !strcmp(argv[2], "status")) {
        string addr;
        string pass;
        if (GetInfoFromFile(addr, pass)) {
            printf("Eamil Address: %s\nPassword: %s\n", addr.c_str(), pass.c_str());
        }
        else printf("you did not bind before\n");
    }
    else if ((argc == 2 || argc == 3) && !strcmp(argv[1], "list")) {
        try {
            string addr;
            string pass;
            if (GetInfoFromFile(addr, pass)) {
                POP3Connector pop3(addr, pass);
                if (argc == 2) pop3.List();
                else {
                    int t;
                    if (isNumber(argv[2])) {
                        sscanf(argv[2], "%d", &t);
                        pop3.List(t);
                    }
                    else printf("please input a number(>-1) after list\n");
                }
            }
            else printf("you did not bind before\n");
        }
        catch (Exception e) {
            e.Print();
            return 0;
        }
    }
    else if (argc == 3 && !strcmp(argv[1], "retr")) {
        try {
            string addr;
            string pass;
            if (GetInfoFromFile(addr, pass)) {
                POP3Connector pop3(addr, pass);
                int id = 0;
                if (isNumber(argv[2])) {
                    sscanf(argv[2], "%d", &id);
                    pop3.Retr(id);
                }
                else printf("please input a number(>-1) after retr\n");
            }
            else printf("you did not bind before\n");
        }
        catch (Exception e) {
            e.Print();
            return 0;
        }
    }
    else if ((argc == 4 || argc == 5 || argc == 6) && !strcmp(argv[1], "send")) {
        try {
            string addr;
            string pass;
            //myemail send username@xxxx.xx contentfilename [utf-8] [attachmentfilename]
            if (GetInfoFromFile(addr, pass)) {


                string charset = "gb18030";
                MIMEContent cont("multipart/alternative;", "8Bit");
                if (argc == 4) {
                    cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
                }
                else if (argc > 4 && string(argv[4]) == "utf-8") {
                    charset = "utf-8";
                    cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
                    if (argc == 6) {
                        cont.AddMIMEContent(new MIMEContent("application/octet-stream;",
                            "base64", argv[5], "attachment;"));
                    }
                }
                else if (argc == 5) {
                    cont.AddMIMEContent(new MIMEContent("text/plain; charset=\"" + charset + "\";", "base64", argv[3]));
                    cont.AddMIMEContent(new MIMEContent("application/octet-stream;",
                        "base64", argv[4], "attachment;"));
                }
                else throw Exception("only can attach one attachment");

                string content = Email().SetFrom(addr).SetTo(argv[2]).SetSubject(argv[3])
                    .SetMIMEContent(&cont)
                    .BuidEmailString();
                SMTPConnector smtp(addr, pass);
                if (!smtp.SendEmail(argv[2], content)) {
                    throw Exception("send email failed");
                }
                else printf("send success!\n");

            }
            else printf("you did not bind before\n");
        }
        catch (Exception e) {
            e.Print();
            return 0;
        }
    }
    else {
        printf("unable to parse command\n");
        WSACleanup();
        return 0;
    }
    return 1;
}
